<?php
// +----------------------------------------------------------------------
// | HkCms 应用中心
// +----------------------------------------------------------------------
// | Copyright (c) 2020-2021 http://www.hkcms.cn, All rights reserved.
// +----------------------------------------------------------------------
// | Author: 广州恒企教育科技有限公司 <admin@hkcms.cn>
// +----------------------------------------------------------------------

declare (strict_types=1);

namespace app\admin\controller;

use app\admin\model\App;
use app\admin\model\routine\Config;
use think\addons\AddonsException;
use think\addons\Cloud;
use think\facade\Db;
use think\facade\Validate;

class Appcenter extends BaseController
{
    /**
     * 权限中间件
     * @var array
     */
    protected $middleware = [
        'login',
        'auth' => ['only'=>['index','online']]
    ];

    /**
     * 初始化
     */
    protected function initialize()
    {
        parent::initialize();

        if (!$this->config->get('cms.appcenter')) { // 是否开启应用中心(/config/cms.php)
            $this->error(lang('Application center is closed'),'','',0);
        }
    }

    /**
     * 本地列表
     * @return string|\think\response\Json|void
     */
    public function index()
    {
        if ($this->request->isAjax()) {
            $type = $this->request->param('type');

            // 获取安装信息
            $model = App::where(['type'=>$type]);
            if ($type=='template') {
                $module = $this->request->param('module');
                $model = $model->where(['module'=>$module]);
            }
            $data = $model->select()->toArray();

            // 查出未安装的插件。
            $all = get_addons_info_all($type);
            if (!empty($all)) {
                if (!empty($module)) {
                    $all = $all[$module];
                }
                $newData = [];
                foreach ($all as $key=>$value) {
                    $bl = true;
                    foreach ($data as $k=>$v) {
                        if ($key==$v['name']) {
                            $bl = false;
                            break;
                        }
                    }
                    if ($bl) {
                        $value['db_install'] = false;
                        $newData[] = $value;
                    }
                }
                $data = array_merge($newData, $data);
            }

            if (!empty($data)) {
                // 根据本地的插件获取线上插件的信息
                $nameArr = [];
                foreach ($data as $key=>$value) {
                    $nameArr[] = $value['name'];

                    // 获取预览图
                    if (empty($value['image']) && $value['type']=='template') {
                        $previewPath = config('cms.tpl_static').$value['module'].DIRECTORY_SEPARATOR.$value['name'].DIRECTORY_SEPARATOR.'preview.jpg';
                        if (is_file($previewPath)) {
                            $data[$key]['image'] = str_replace('\\', '/', '/' . str_replace(public_path(), "", $previewPath));
                        } else {
                            $data[$key]['image'] = '/static/common/image/nopic.png';
                        }
                    } else if (empty($value['image'])) {
                        $data[$key]['image'] = '/static/common/image/nopic.png';
                    }

                    if (empty($data[$key]['config'])) {
                        // 获取配置
                        $data[$key]['config'] = get_addons_config($type, $value['name'], $value['module']??'');
                    }
                }
                try {
                    $Info = Cloud::getInstance()->getInfos(['name'=>implode(',', $nameArr),'type'=>$type]);
                } catch (\Exception $exception) {
                    $Info = false;
                }

                $this->view->assign('info', $Info);
            } else {
                $data = $all;
            }

            $this->view->assign('data', $data);
            $this->view->assign('type', $type);
            $this->view->assign('module', $module??'');
            $this->view->layout(false);
            $html = view('appcenter/local')->getContent();
            $this->success('','',['html'=>$html]);
        }
        $active = $this->request->param('module','index');

        // 应用中心登录信息检测
        $config['cloud_username'] = $this->cache->get('cloud_username');
        $config['cloud_password'] = $this->cache->get('cloud_password');
        if (!empty($config['cloud_username']) && !empty($config['cloud_password'])) {
            try {
                Cloud::getInstance()->chekcUser($config['cloud_username'], $config['cloud_password']);
            } catch (AddonsException $exception) {
            }
        }

        $module = App::field('module')->where(['type'=>'template'])->group('module')->select()->toArray();
        return $this->view->fetch('',['module'=>$module,'active'=>$active]);
    }

    /**
     * 主题切换
     */
    public function setTheme()
    {
        $name = $this->request->param('name');
        $module = $this->request->param('module');
        if (empty($name) || empty($module)) {
            $this->error(lang('Parameter is empty'));
        }

        $res = Db::name('config')->where(['name'=>$module.'_theme'])->update(['value'=>$name]);
        if ($res) {
            $this->cache->delete('site');
            hook('theme_change', $module.'_theme');
            $this->success();
        }
        $this->error(lang('Unchanged'));
    }

    /**
     * 在线列表
     * @return string|void
     * @throws \Exception
     */
    public function online()
    {
        if ($this->request->isAjax()) {
            $param = $this->request->param('param');
            parse_str($param, $arr);
            if (empty($arr['type'])) {
                $this->error(lang('%s not null',['type']));
            }
            $list = Cloud::getInstance()->getList($arr);
            if (!empty($list['row'])) {
                foreach ($list['row'] as $key=>$value) {
                    $one = App::where(['name'=>$value['name']])->find();
                    if (!empty($one)) {
                        // 已安装
                        $list['row'][$key]['is_install'] = 1;
                        $list['row'][$key]['installVersion'] = $one['version'];
                        $list['row'][$key]['status'] = $one['status'];
                        $list['row'][$key]['upgradeData'] = get_upgrade($one['version'], $value['version']);
                    } else {
                        // 未安装
                        $list['row'][$key]['is_install'] = 0;
                    }
                }
            }

            $this->view->layout(false);
            $this->view->assign('data', $list);
            $this->view->assign('param', $arr);
            $html = view('appcenter/online_list')->getContent();
            $this->success('','',['html'=>$html,'total'=>$list['total']??0,'page'=>$list['page']??'']);
        } else if ($this->request->isPost()) {
            $username = $this->request->param('username');
            $password = $this->request->param('password');
            $remember = $this->request->param('remember','');
            if (empty($username) || empty($password)) {
                $this->error('请填写完整');
            }

            try {
                Cloud::getInstance()->chekcUser($username, $password);
            } catch (AddonsException $exception) {
                $this->error($exception->getMessage());
            }

            if ($remember) {    // 记住账号密码
                Config::where(['name'=>'cloud_username'])->save(['value'=>$username]);
                Config::where(['name'=>'cloud_password'])->save(['value'=>$password]);
                Config::initConfig(true);
            }
            return redirect((string)url('/appcenter/online'));
        }

        // 应用中心登录信息检测
        $config['cloud_username'] = $this->cache->get('cloud_username');
        $config['cloud_password'] = $this->cache->get('cloud_password');
        if ((empty($config['cloud_username']) || empty($config['cloud_password'])) && empty($this->cache->get('cloud_token'))) {
            $this->view->layout(false);
            return $this->view->fetch('login');
        } else if ((!empty($config['cloud_username']) && !empty($config['cloud_password'])) && empty($this->cache->get('cloud_token'))) {
            try {
                Cloud::getInstance()->chekcUser($config['cloud_username'], $config['cloud_password']);
            } catch (AddonsException $exception) {
                $this->view->layout(false);
                return $this->view->fetch('login',['msg'=>'错误信息：'.$exception->getMessage()]);
            }
        }
        $this->view->assign('ac_user', $this->cache->get('cloud_token'));
        return $this->view->fetch();
    }

    /**
     * 安装本地插件，非上传的形式
     */
    public function installDb()
    {
        $name = $this->request->param('name');
        $type = $this->request->param('type');
        $module = $this->request->param('module');
        if (empty($name)) {
            $this->error(lang('The plug-in ID cannot be empty'));
        }
        if (!Validate::is($name, '/^[a-zA-Z][a-zA-Z0-9_]*$/')) {
            $this->error(lang('Incorrect plug-in ID format'));
        }
        if (empty($type)) {
            $this->error(lang('%s not null',['type']));
        }
        if ($type=='template' && empty($module)) {
            $this->error(lang('%s not null',['module']));
        }

        // 获取信息
        $info = get_addons_info($name, $type, $module);
        if (empty($info)) {
            $this->error(lang('Application information does not exist'));
        }

        // 检查是否已经安装
        $one = App::where(['name'=>$name])->find();
        if (!empty($one)) {
            $this->error(lang('%s not null',['type']));
        }

        // 录入数据库
        $info['status'] = 1;
        $info['type'] = $type;

        Db::startTrans();
        try {
            (new App)->addInstall($info);
            if ($type!='template') {
                $obj = get_addons_instance($name);
                if (!empty($obj)) { // 调用插件安装
                    $obj->install();
                }
                Cloud::getInstance()->enable($name);
            }
            Db::commit();
        } catch (AddonsException $e) {
            Db::rollback();
            $this->error($e->getMessage());
        }

        app()->cache->clear();
        $this->success(lang('Installation successful'));
    }

    /**
     * 离线安装
     */
    public function local()
    {
        $type = $this->request->param('type');
        $files = $this->request->file('files');
        if (empty($type)) {
            $this->error(lang('%s not null',['type']));
        }
        if (empty($files) || empty($files[0])) {
            $this->error(lang('Please upload the compressed package'));
        }
        $value = $files[0];

        try {
            validate(
                [
                    'files' => [
                        // 限制文件大小(单位b)
                        'fileSize' => $this->cache->get('site')['file_size'],
                        // 限制文件后缀，多个后缀以英文逗号分割
                        'fileExt'  => 'zip'
                    ]
                ], [
                    'files.fileSize' => lang('File cannot exceed %s',[($this->cache->get('site')['file_size']/1024/1024).'MB']),
                    'files.fileExt' => lang('Only supports zip files'),
                ]
            )->check(['files'=>$value]);

            // 保存文件
            $file = $this->app->filesystem->disk('local')->putFile(date('Y-m'), $value, 'md5');
            if (!$file) {
                $this->error(lang('File save failed'));
            }
            $file = str_replace(['\\','/'],DIRECTORY_SEPARATOR, $this->app->filesystem->getDiskConfig('local', 'root').DIRECTORY_SEPARATOR.$file);
        } catch (\think\exception\ValidateException $e) {
            $this->error($e->getMessage());
        }

        $info = Cloud::getInstance()->installLocal($type, $file);
        $info['type'] = $type;
        (new App)->addInstall($info);

        app()->cache->clear();
        $this->success('','', []);
    }

    /**
     * 插件打包
     * @throws \think\addons\AddonsException
     */
    public function pack()
    {
        $type = $this->request->param('type');
        $name = $this->request->param('name');
        if (empty($name)) {
            $this->error(lang('The plug-in ID cannot be empty'));
        }
        if (!Validate::is($name, '/^[a-zA-Z][a-zA-Z0-9_]*$/')) {
            $this->error(lang('Incorrect plug-in ID format'));
        }

        if ('addon'==$type) {
            $info = get_addons_info($name);
            if (empty($info)) {
                $this->error(lang('Application information does not exist'));
            }
            Cloud::getInstance()->pack($this->app->addons->getAddonsPath().$name.DIRECTORY_SEPARATOR);
        }
    }

    /**
     * 在线安装
     * @throws \think\addons\AddonsException
     */
    public function install()
    {
        if ($this->request->isAjax()) {
            $name = $this->request->param('name');
            $version = $this->request->param('version','');
            if (empty($name)) {
                $this->error(lang('The plug-in ID cannot be empty'));
            }
            if (!Validate::is($name, '/^[a-zA-Z][a-zA-Z0-9_]*$/')) {
                $this->error(lang('Incorrect plug-in ID format'));
            }

            $info = Cloud::getInstance()->getInfo(['name'=>$name, 'version'=>$version]);
            // 版本信息判断
            if (!version_compare($this->config->get('ver.cms_version'),$info['version']['cms_version'], $info['version']['cms_version_op'])) {
                $this->error(lang('The HkCms supported by this app are %s',["{$info['version']['cms_version_op']} {$info['version']['cms_version']}"]));
            }

            Cloud::getInstance()->install($info);
            (new App)->addInstall($info);
            $this->app->cache->tag('addons')->clear();
            $this->success(lang('Installation successful'));
        }
        $this->error(lang('Illegal request'));
    }

    /**
     * 在线/本地卸载
     */
    public function uninstall()
    {
        if ($this->request->isAjax()) {
            $name = $this->request->param('name','');
            $type = $this->request->param('type','');
            $module = $this->request->param('module','');

            if (!Validate::is($name, '/^[a-zA-Z][a-zA-Z0-9_]*$/')) {
                $this->error(lang('Incorrect plug-in ID format'));
            }
            if (empty($name) || ($type!='template' && $type!='addon' && $type!='module') || ($type=='template' && empty($module))) {
                $this->error(lang('Abnormal value of required parameter'));
            }

            // 获取已安装的插件信息
            $addonInfo = App::where(['type'=>$type,'name'=>$name])->find();
            if (empty($addonInfo)) {
                $this->error(lang('%s is not installed', [$name]));
            }

            if ('template'==$type) { // 卸载模板的情况，检测是否已经设置为主题了
                $config = $this->cache->get('site');
                $theme = $config[$module.'_theme'] ?? '';
                if ($theme==$name) {
                    $this->error(lang('The current template has been set as the theme! Please cancel and try again~'));
                }
            } else {
                if ($addonInfo['status']==1) {
                    $this->error(lang('Please disable the plugin and try again'));
                }
            }

            Cloud::getInstance()->uninstall(['name'=>$name,'type'=>$type,'module'=>$module]);
            App::where(['name'=>$name])->delete();
            $this->app->cache->tag('addons')->clear();
            $this->success(lang('Successfully uninstalled'));
        }
        $this->error(lang('Illegal request'));
    }

    /**
     * 版本在线/本地更新
     * @throws \think\addons\AddonsException
     */
    public function upgrade()
    {
        if ($this->request->isAjax()) {
            $name = $this->request->param('name');
            $version = $this->request->param('version','');
            if (empty($name)) {
                $this->error(lang('The plug-in ID cannot be empty'));
            }
            if (!Validate::is($name, '/^[a-zA-Z][a-zA-Z0-9_]*$/')) {
                $this->error(lang('Incorrect plug-in ID format'));
            }

            $info = Cloud::getInstance()->getInfo(['name'=>$name, 'version'=>$version]);

            $addonInfo = App::where(['name'=>$info['name']])->find();
            if (empty($addonInfo)) {
                $this->error(lang('%s is not installed', [$info['name']]));
            }
            $info['status'] = $addonInfo['status'];

            // 版本信息判断
            if (!version_compare($this->config->get('ver.cms_version'),$info['version']['cms_version'], $info['version']['cms_version_op'])) {
                $this->error(lang('The HkCms supported by this app are %s',["{$info['version']['cms_version_op']} {$info['version']['cms_version']}"]));
            }
            if (empty($addonInfo['version'])) {
                $this->error(lang('Failed to obtain plug-in version information'));
            }
            // 插件版本信息判断
            if (!version_compare($info['version']['version'], $addonInfo['version'],'>')) {
                $this->error(lang('Note that the installed version: %s',[$addonInfo['version']]));
            }

            Cloud::getInstance()->upgrade($info);
            (new App)->editInstall($info);
            $this->app->cache->tag('addons')->clear();
            $this->success(lang('Installation successful'));
        }
        $this->error(lang('Illegal request'));
    }

    /**
     * 插件配置
     * @return string|void
     */
    public function setConfig()
    {
        $name = $this->request->param('name');
        $type = $this->request->param('type','');
        $module = $this->request->param('module','');
        if (empty($name)) {
            $this->error(lang('The plug-in ID cannot be empty'));
        }
        if (!Validate::is($name, '/^[a-zA-Z][a-zA-Z0-9_]*$/')) {
            $this->error(lang('Incorrect plug-in ID format'));
        }

        $addonInfo = App::where(['name'=>$name,'type'=>$type])->find();
        if (empty($addonInfo)) {
            $this->error(lang('%s is not installed', [$name]));
        }

        $data = get_addons_config($type, $name, $module, true); // 获取整个配置文件
        $this->view->assign('sdata', $data); // 写入源数据

        if ($this->request->isPost()) {
            $post = $this->request->post('row');
            if (empty($post)) {
                $this->error(lang('The requested data is empty'));
            }

            foreach ($data as $key=>&$value) {
                if (!empty($value['item'])) { // 分组写法
                    if (isset($post[$key])) {
                        foreach ($value['item'] as $k=>&$v) {
                            if (isset($post[$key][$k])) {
                                if (is_array($post[$key][$k])) {
                                    $v['value'] = implode(',', $post[$key][$k]);
                                } else {
                                    $v['value'] = $post[$key][$k];
                                }
                            }
                        }
                    }
                } else {
                    if (isset($post[$key])) {
                        if (is_array($post[$key])) {
                            $value['value'] = implode(',', $post[$key]);
                        } else {
                            $value['value'] = $post[$key];
                        }
                    }
                }
            }

            App::where(['name'=>$name,'type'=>$type])->save(['config'=>json_encode($data)]);
            $this->app->cache->tag('get_addons_config')->clear();

            hook('addon_config_save', $data);
            $this->success();
        }

        // 生成表单
        $this->view->layout(false);
        foreach ($data as $key=>$value) {
            if (empty($value['item'])) { // 一级
                $data = $this->view->fetch('appcenter/field', ['data'=>$data,'group'=>'']);
                break;
            } else {
                $data[$key]['item'] = $this->view->fetch('appcenter/field', ['data'=>$value['item'],'group'=>$key]);
            }
        }

        $this->view->layout(true);
        $this->view->assign('data', $data);
        $this->view->assign('module', $module);

        $path = $type=='template'?config('cms.tpl_path'). $module . DIRECTORY_SEPARATOR . $name . DIRECTORY_SEPARATOR . 'config.html':app('addons')->getAddonsPath().$name.DIRECTORY_SEPARATOR.'config.html';
        return is_file($path) ? $this->view->fetch($path) : $this->view->fetch();
    }

    /**
     * 在线启用禁用
     * @throws \think\addons\AddonsException
     */
    public function enable()
    {
        if ($this->request->isAjax()) {
            $name = $this->request->param('name');
            $type = $this->request->param('type');
            $value = $this->request->param('value');
            if (empty($name)) {
                $this->error(lang('The plug-in ID cannot be empty'));
            }
            if (!Validate::is($name, '/^[a-zA-Z][a-zA-Z0-9_]*$/')) {
                $this->error(lang('Incorrect plug-in ID format'));
            }
            if ($value!=-1 && $value!=1) {
                $this->error(lang('Operation failed'));
            }

            $addonInfo = App::where(['name'=>$name])->find();
            if (empty($addonInfo)) {
                $this->error(lang('%s is not installed', [$addonInfo['name']]));
            }

            if ($addonInfo['status']==$value) {
                $this->error(lang('The plugin is already %s',[($addonInfo['status']==1?'启用':'禁用')]));
            }

            if ($value==1) { // 启用
                Cloud::getInstance()->enable($name);
                App::where(['name'=>$name])->save(['status'=>1]);
                $this->app->cache->tag('addons')->clear();
            } else { // 禁用
                Cloud::getInstance()->disable($name);
                App::where(['name'=>$name])->save(['status'=>-1]);
                $this->app->cache->tag('addons')->clear();
            }

            $this->success();
        }
        $this->error(lang('Illegal request'));
    }

    /**
     * 获取筛选
     */
    public function getFilter()
    {
        $type = $this->request->get('type');
        $data = Cloud::getInstance()->getFilter($type);
        $this->success('', '', $data);
    }
}